/*
 * @brief PLL test (implementation file)
 *
 * @note
 * Copyright(C) NXP Semiconductors, 2015
 * All rights reserved.
 *
 * @par
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * LPC products.  This software is supplied "AS IS" without any warranties of
 * any kind, and NXP Semiconductors and its licensor disclaim any and
 * all warranties, express or implied, including all implied warranties of
 * merchantability, fitness for a particular purpose and non-infringement of
 * intellectual property rights.  NXP Semiconductors assumes no responsibility
 * or liability for the use of the software, conveys no license or rights under any
 * patent, copyright, mask work right, or any other intellectual property rights in
 * or to any products. NXP Semiconductors reserves the right to make changes
 * in the software without notification. NXP Semiconductors also makes no
 * representation or warranty that such application will be suitable for the
 * specified use without further testing or modification.
 *
 * @par
 * Permission to use, copy, modify, and distribute this software and its
 * documentation is hereby granted, under NXP Semiconductors' and its
 * licensor's relevant copyrights in the software, without fee, provided that it
 * is used in conjunction with NXP Semiconductors microcontrollers.  This
 * copyright, permission, and disclaimer notice must appear in all copies of
 * this code.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <popt.h>

#include "pll_const.h"
#include "pll_codec.h"
#include "pll_test.h"

/*****************************************************************************
 * Public types/enumerations/variables
 ****************************************************************************/

/*****************************************************************************
 * Private types/enumerations/variables
 ****************************************************************************/
#define OPT_ABOUT			0x0001

#define OPT_TESTS			0x0010

#define OPT_ENC_N			0x0100
#define OPT_ENC_M			0x0200
#define OPT_ENC_P			0x0400

#define OPT_DEC_N			0x1000
#define OPT_DEC_M			0x2000
#define OPT_DEC_P			0x4000

static void about(void);
static void nmp_tests(void);
static void fn_enc_n(uint32_t n_dec);
static void fn_enc_m(uint32_t m_dec);
static void fn_enc_p(uint32_t p_dec);
static void fn_dec_n(uint32_t n_enc);
static void fn_dec_m(uint32_t m_enc);
static void fn_dec_p(uint32_t p_enc);

static poptContext	optCon;

/*****************************************************************************
 * Private functions
 ****************************************************************************/

static void about(void)
{
	printf("PLL test:\n");
	printf("Copyright (C) NXP Semiconductors, 2015\n");
	printf("All rights reserved.\n");
	printf("version: 0.0.1.0\n");
}

/*****************************************************************************
 * Public functions
 ****************************************************************************/


//----------------------------------------------------------------------------
// Main function
//----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
	uint32_t	opt=0;
	int			idx=0;

	uint32_t	in_n_enc=0, in_m_enc=0, in_p_enc=0;
	uint32_t	in_n_dec=0, in_m_dec=0, in_p_dec=0;

	struct poptOption optionsTable[] = {
		{ "about",		'a', POPT_ARG_NONE,	  NULL,			'a', "display information about the app",			NULL	},
		{ "tests",		't', POPT_ARG_NONE,	  NULL,			't', "Run N, M, and P tests on encode/decode",		NULL	},

		{ "enc-n",		'n', POPT_ARG_INT,	  &in_n_dec,	'n', "Encode N",									NULL	},
		{ "enc-m",		'm', POPT_ARG_INT,	  &in_m_dec,	'm', "Encode M",									NULL	},
		{ "enc-p",		'p', POPT_ARG_INT,	  &in_p_dec,	'p', "Encode P",									NULL	},

		{ "dec-n",		'N', POPT_ARG_INT,	  &in_n_enc,	'N', "Decode N",									NULL	},
		{ "dec-m",		'M', POPT_ARG_INT,	  &in_m_enc,	'M', "Decode M",									NULL	},
		{ "dec-p",		'P', POPT_ARG_INT,	  &in_p_enc,	'P', "Decode P",									NULL	},

		POPT_AUTOHELP
		POPT_TABLEEND
	};

//------------------------------------------------------------------------------
//  No buffering for Eclipse/CDT (bug 173732)
//
//	This won't harm anything but it's not required
//	for normal console operation.
//
//	Remove if you aren't using Eclipse/CDT.
//------------------------------------------------------------------------------
//	setvbuf(stdout, NULL, _IONBF, 0);
//	setvbuf(stdin, NULL, _IONBF, 0);

//------------------------------------------------------------------------------
//  Process arguments
//------------------------------------------------------------------------------
	optCon = poptGetContext(NULL, argc, (const char **)argv, optionsTable, 0);
	poptSetOtherOptionHelp(optCon, "[OPTIONS]*");
	while ((idx = poptGetNextOpt(optCon)) >= 0) {
		switch (idx) {
		case 'a':	opt |= OPT_ABOUT;		break;
		case 't':	opt |= OPT_TESTS;		break;

		case 'n':	opt |= OPT_ENC_N;		break;
		case 'm':	opt |= OPT_ENC_M;		break;
		case 'p':	opt |= OPT_ENC_P;		break;

		case 'N':	opt |= OPT_DEC_N;		break;
		case 'M':	opt |= OPT_DEC_M;		break;
		case 'P':	opt |= OPT_DEC_P;		break;
		}
	}
	if (idx < -1) {
		fprintf(stderr, "%s: %s\n",
		        poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
		        poptStrerror(idx));
		exit(opt);
	}

//------------------------------------------------------------------------------
//  Parameter execution
//------------------------------------------------------------------------------
	if (opt & OPT_ABOUT) {
		about();
		exit(0);
	}

	if (opt & OPT_TESTS) {
		nmp_tests();
		exit(0);
	}

	if (opt & OPT_ENC_N) {	fn_enc_n(in_n_dec);		}
	if (opt & OPT_ENC_M) {	fn_enc_m(in_m_dec);		}
	if (opt & OPT_ENC_P) {	fn_enc_p(in_p_dec);		}

	if (opt & OPT_DEC_N) {	fn_dec_n(in_n_enc);		}
	if (opt & OPT_DEC_M) {	fn_dec_m(in_m_enc);		}
	if (opt & OPT_DEC_P) {	fn_dec_p(in_p_enc);		}
}

//------------------------------------------------------------------------------
//  Tests
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//  N Test
//------------------------------------------------------------------------------
static void nmp_tests(void)
{
	uint32_t	n_enc, n_dec, n_tst;
	uint32_t	m_enc, m_dec, m_tst;
	uint32_t	p_enc, p_dec, p_tst;

	uint32_t	pass_ct=0, fail_ct=0;

	printf("N test:\n");
	for (n_dec=0; n_dec<NVALMAX; n_dec++) {
		n_enc = pllEncodeN(n_dec);
		n_tst = pllDecodeN(n_enc);

		if (n_tst == n_dec) {
			//printf("Pass: decoded:%d encoded: %d\n", n_dec, n_enc);
			pass_ct += 1;
		}
		else {
			printf("Fail: decoded:%d encoded: %d\n", n_dec, n_enc);
			fail_ct += 1;
		}
	}

//------------------------------------------------------------------------------
//  M Test
//------------------------------------------------------------------------------
	printf("M test:\n");
	for (m_dec=0; m_dec<MVALMAX; m_dec++) {
		m_enc = pllEncodeM(m_dec);
		m_tst = pllDecodeM(m_enc);

		if (m_tst == m_dec) {
			//printf("Pass: decoded:%d encoded: %d\n", m_dec, m_enc);
			pass_ct += 1;
		}
		else {
			printf("Fail: decoded:%d encoded: %d\n", m_dec, m_enc);
			fail_ct += 1;
		}
	}

//------------------------------------------------------------------------------
//  M Test
//------------------------------------------------------------------------------
	printf("P test:\n");
	for (p_dec=0; p_dec<PVALMAX; p_dec++) {
		p_enc = pllEncodeM(p_dec);
		p_tst = pllDecodeM(p_enc);

		if (p_tst == p_dec) {
			//printf("Pass: decoded:%d encoded: %d\n", p_dec, p_enc);
			pass_ct += 1;
		}
		else {
			printf("Fail: decoded:%d encoded: %d\n", p_dec, p_enc);
			fail_ct += 1;
		}
	}

//------------------------------------------------------------------------------
//  Statistics
//------------------------------------------------------------------------------
	printf("Test statistics:\n");
	printf(" - Pass count: %d\n - Fail count: %d\n", pass_ct, fail_ct);
}

//------------------------------------------------------------------------------
//  Encode N
//------------------------------------------------------------------------------
static void fn_enc_n(uint32_t n_dec)
{
	uint32_t n_enc = pllEncodeN(n_dec);

	if (n_dec > NVALMAX) {
		printf("M value out of range: %d %d\n",n_dec, NVALMAX);
	} else {
		printf("N encode:\n");
		printf("  initial value: %d (0x%04x)\n",n_dec, n_dec);
		printf("  encoded value: %d (0x%04x)\n",n_enc, n_enc);
	}
}

//------------------------------------------------------------------------------
//  Encode M
//------------------------------------------------------------------------------
static void fn_enc_m(uint32_t m_dec)
{
	uint32_t m_enc = pllEncodeM(m_dec);

	if (m_dec > MVALMAX) {
		printf("M value out of range: %d %d\n",m_dec, MVALMAX);
	} else {
		printf("M encode:\n");
		printf("  initial value: %d (0x%04x)\n",m_dec, m_dec);
		printf("  encoded value: %d (0x%04x)\n",m_enc, m_enc);
	}
}

//------------------------------------------------------------------------------
//  Encode P
//------------------------------------------------------------------------------
static void fn_enc_p(uint32_t p_dec)
{
	uint32_t p_enc = pllEncodeP(p_dec);

	if (p_dec > PVALMAX) {
		printf("P value out of range: %d %d\n",p_dec, PVALMAX);
	} else {
		printf("P encode:\n");
		printf("  initial value: %d (0x%04x)\n",p_dec, p_dec);
		printf("  encoded value: %d (0x%04x)\n",p_enc, p_enc);
	}
}

//------------------------------------------------------------------------------
//  Decode N
//------------------------------------------------------------------------------
static void fn_dec_n(uint32_t n_enc)
{
	uint32_t n_dec = pllDecodeN(n_enc);

	printf("N decode:\n");
	printf("  initial value: %d (0x%04x)\n",n_enc, n_enc);
	printf("  decoded value: %d (0x%04x)\n",n_dec, n_dec);
}

//------------------------------------------------------------------------------
//  Decode M
//------------------------------------------------------------------------------
static void fn_dec_m(uint32_t m_enc)
{
	uint32_t m_dec = pllDecodeM(m_enc);

	printf("M decode:\n");
	printf("  initial value: %d (0x%04x)\n",m_enc, m_enc);
	printf("  decoded value: %d (0x%04x)\n",m_dec, m_dec);
}

//------------------------------------------------------------------------------
//  Decode P
//------------------------------------------------------------------------------
static void fn_dec_p(uint32_t p_enc)
{
	uint32_t p_dec = pllDecodeP(p_enc);

	printf("P decode:\n");
	printf("  initial value: %d (0x%04x)\n",p_enc, p_enc);
	printf("  decoded value: %d (0x%04x)\n",p_dec, p_dec);
}
